package com.iflytek.jcy.supervise.job.component.config;

import java.time.Instant;
import java.time.LocalDateTime;
import java.util.Date;

import cn.hutool.core.date.LocalDateTimeUtil;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.iflytek.jcy.supervise.job.common.util.IdWokerUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

/**
 * 数据库相关配置
 *
 * @author <a href=mailto:ktyi@iflytek.com>伊开堂</a>
 * @date 2019/4/15 11:06
 */
@Configuration
public class MyBatisPlusConfig {

    private DbType dbType = DbType.MYSQL;
    private String prefix = null;

    @Autowired
    public void setEnvironment(Environment environment) {
        String driverClassName = environment.getProperty("spring.datasource.driver-class-name");
        // 通过 JDBC驱动确定是否为达梦数据库
        if (StringUtils.isNotBlank(driverClassName)) {
            switch (driverClassName) {
                case "dm.jdbc.driver.DmDriver":
                    dbType = DbType.DM;
                    break;
                case "oracle.jdbc.driver.OracleDriver":
                    dbType = DbType.ORACLE_12C;
                    break;
                default:
                    System.out.println("use mysql as db type by default");
            }
        }

        prefix = environment.getProperty("mybatis-plus.global-config.db-config.table-prefix");
    }

    /**
     * 分页插件
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        PaginationInnerInterceptor innerInterceptor = new PaginationInnerInterceptor(dbType);
        innerInterceptor.setMaxLimit(1000L);
        interceptor.addInnerInterceptor(innerInterceptor);
        return interceptor;
    }

    /*
    *@TableField(value = "create_time", fill = FieldFill.INSERT)
    private Date createTime;

    @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    */
    @Bean
    public MetaObjectHandler metaObjectHandler() {
        // 插入时自动注入createTime/updateTime, 更新时自动注入updateTime
        return new MetaObjectHandler() {
            private static final String CREATE_TIME_FIELD = "createTime";
            private static final String UPDATE_TIME_FIELD = "updateTime";

            @Override
            public void insertFill(MetaObject metaObject) {
                Class<?> createTimeType = metaObject.getSetterType(CREATE_TIME_FIELD);
                Class<?> updateTimeType = metaObject.getSetterType(UPDATE_TIME_FIELD);
                Object createTime = this.getFieldValByName(CREATE_TIME_FIELD, metaObject);
                Object updateTime = this.getFieldValByName(UPDATE_TIME_FIELD, metaObject);

                if (LocalDateTime.class.equals(createTimeType)) {
                    LocalDateTime now = LocalDateTime.now();
                    if (createTime == null) {
                        this.strictInsertFill(metaObject, CREATE_TIME_FIELD, LocalDateTime.class, now);
                    }

                    filleUpdateTime(metaObject, updateTimeType, updateTime, LocalDateTimeUtil.toEpochMilli(now));
                }
                else if (Instant.class.equals(createTimeType)) {
                    Instant now = Instant.now();
                    if (createTime == null) {
                        this.strictInsertFill(metaObject, CREATE_TIME_FIELD, Instant.class, now);
                    }

                    filleUpdateTime(metaObject, updateTimeType, updateTime, LocalDateTimeUtil.toEpochMilli(now));
                }
                else if (Date.class.equals(createTimeType)) {
                    Date now = new Date();
                    if (createTime == null) {
                        this.strictInsertFill(metaObject, CREATE_TIME_FIELD, Date.class, now);
                    }

                    filleUpdateTime(metaObject, updateTimeType, updateTime, now.getTime());
                }
            }

            private void filleUpdateTime(MetaObject metaObject, Class<?> fieldType, Object fieldValue, long timestamp) {
                if (fieldValue == null) {
                    if (LocalDateTime.class.equals(fieldType)) {
                        this.strictInsertFill(metaObject, UPDATE_TIME_FIELD, LocalDateTime.class,
                                LocalDateTimeUtil.of(timestamp));
                    }
                    else if (Instant.class.equals(fieldType)) {
                        this.strictInsertFill(metaObject, UPDATE_TIME_FIELD, Instant.class,
                                Instant.ofEpochMilli(timestamp));
                    }
                    else if (Date.class.equals(fieldType)) {
                        this.strictInsertFill(metaObject, UPDATE_TIME_FIELD, Date.class, new Date(timestamp));
                    }
                }
            }

            @Override
            public void updateFill(MetaObject metaObject) {
                Class<?> fieldType = metaObject.getSetterType(UPDATE_TIME_FIELD);
                Object updateTime = this.getFieldValByName(UPDATE_TIME_FIELD, metaObject);
                if (updateTime == null) {
                    if (LocalDateTime.class.equals(fieldType)) {
                        this.strictUpdateFill(metaObject, UPDATE_TIME_FIELD, LocalDateTime.class, LocalDateTime.now());
                    }
                    else if (Instant.class.equals(fieldType)) {
                        this.strictUpdateFill(metaObject, UPDATE_TIME_FIELD, Instant.class, Instant.now());
                    }
                    else if (Date.class.equals(fieldType)) {
                        this.strictUpdateFill(metaObject, UPDATE_TIME_FIELD, Date.class, new Date());
                    }
                }
            }
        };
    }

    /*
    实体的主键生成策略必须使用INPUT
    @KeySequence(value = "SEQ_ORACLE_STRING_KEY", clazz = String.class)
    public class YourEntity {

        @TableId(value = "ID_STR", type = IdType.INPUT)
        private String idStr;

    }
    */
    @Bean
    public IKeyGenerator keyGenerator() {
        return new IKeyGenerator() {
            @Override
            public String executeSql(String incrementerName) {
                return String.valueOf(IdWokerUtil.nextId());
            }

            @Override
            public DbType dbType() {
                return dbType;
            }
        };
    }

    /*
    主键生成策略为ASSIGN_ID
     * */
    @Bean
    public IdentifierGenerator idGenerator() {
        return new IdentifierGenerator() {
            @Override
            public Number nextId(Object entity) {
                return IdWokerUtil.nextId();
            }
        };
    }
}